#include "ProtobufCodecLite.h"

namespace
{
    //检查protobuf版本
    int ProtobufVersionCheck()
    {
        GOOGLE_PROTOBUF_VERIFY_VERSION;
        return 0;
    }
    // 表示该函数或变量可能不使用，这个属性可以避免编译器产生警告信息。
    __attribute__((unused)) int temp = ProtobufVersionCheck();
}

using namespace yhaida;

namespace
{
    const std::string kNoErrorStr = "NoError";
    const std::string kInvalidLengthStr = "InvalidLength";
    const std::string kCheckSumErrorStr = "CheckSumError";
    const std::string kInvalidNameLenStr = "InvalidNameLen";
    const std::string kUnknownMessageTypeStr = "UnknownMessageType";
    const std::string kParseErrorStr = "ParseError";
    const std::string kUnknownErrorStr = "UnknownError";
}

const std::string &ProtobufCodecLite::errorCodeToString(ErrorCode errorCode)
{
    switch (errorCode)
    {
    case ErrorCode::kNoError:
        return kNoErrorStr;
    case ErrorCode::kInvalidLength:
        return kInvalidLengthStr;
    case ErrorCode::kCheckSumError:
        return kCheckSumErrorStr;
    case ErrorCode::kInvalidNameLen:
        return kInvalidNameLenStr;
    case ErrorCode::kUnknownMessageType:
        return kUnknownMessageTypeStr;
    case ErrorCode::kParseError:
        return kParseErrorStr;
    default:
        return kUnknownErrorStr;
    }
}

void ProtobufCodecLite::defaultErrorCallback(const TcpConnectionPtr &conn,
                                             Buffer *,
                                             Timestamp,
                                             ErrorCode errorCode)
{
    LOG_ERROR << "ProtobufCodecLite::defaultErrorCallback - " << errorCodeToString(errorCode);
    if (conn && conn->connected())
    {
        conn->shutdown();
    }
}

//序列化到Buffer
int ProtobufCodecLite::serializeToBuffer(Buffer *buff,
                                         const google::protobuf::Message &message)
{
    GOOGLE_DCHECK(message.IsInitialized()) << InitializationErrorMessage("serialize", message);

#if GOOGLE_PROTOBUF_VERSION > 3009002
    int byte_size = google::protobuf::internal::ToIntSize(message.ByteSizeLong());
#else
    int byte_size = message.ByteSize();
#endif
    buff->ensureWritableBytes(byte_size + kChecksumLen);

    uint8_t *start = reinterpret_cast<uint8_t *>(buff->beginWrite());
    uint8_t *end = message.SerializeWithCachedSizesToArray(start);
    if (end - start != byte_size)
    {
#if GOOGLE_PROTOBUF_VERSION > 3009002
        ByteSizeConsistencyError(byte_size, google::protobuf::internal::ToIntSize(message.ByteSizeLong()), static_cast<int>(end - start));
#else
        ByteSizeConsistencyError(byte_size, message.ByteSize(), static_cast<int>(end - start));
#endif
    }
    buff->hasWritten(byte_size);
    return byte_size;
}

int32_t ProtobufCodecLite::checksum(const void *buff, int len)
{
    return static_cast<int32_t>(
        ::adler32(1, static_cast<const Bytef *>(buff), len));
}
// 1.编码
void ProtobufCodecLite::fillEmptyBuffer(Buffer *buff,
                                        const google::protobuf::Message &message)
{
    assert(buff->readableBytes() == 0);
    // 1.
    buff->append(_tag); // len : _tag.size() -> M
    // 2.
    int len_message = serializeToBuffer(buff, message);
    // 3.
    int32_t checkSum = checksum(buff->peek(), static_cast<int>(buff->readableBytes()));
    buff->appendInt32(checkSum);

    //检验
    assert(buff->readableBytes() == (_tag.size() + len_message + kChecksumLen));
    (void)len_message;

    //封包
    buff->prependInt32(static_cast<int32_t>(buff->readableBytes()));
}
// 2.发送
void ProtobufCodecLite::send(const TcpConnectionPtr &conn,
                             const google::protobuf::Message &message)
{
    //编码
    Buffer buffer;
    fillEmptyBuffer(&buffer, message);
    conn->send(&buffer);
}

//*******************************************************************************
bool ProtobufCodecLite::parseFromBuffer(std::string buff, google::protobuf::Message *message)
{
    return message->ParseFromArray(buff.data(), buff.size());
}

int32_t ProtobufCodecLite::asInt32(const char *buf)
{
    int32_t be32 = 0;
    ::memcpy(&be32, buf, sizeof(be32));
    return sockets::networkToHost32(be32);
}
bool ProtobufCodecLite::validateChecksum(const char *buff, int len)
{
    // check sum
    int32_t expectedCheckSum = asInt32(buff + len - kChecksumLen);
    int32_t checkSum = checksum(buff, len - kChecksumLen);
    return checkSum == expectedCheckSum;
}

ProtobufCodecLite::ErrorCode ProtobufCodecLite::parse(const char *buff, int len_buff, ::google::protobuf::Message *message)
{
    ErrorCode error = ErrorCode::kNoError;

    if (validateChecksum(buff, len_buff))
    {
        //int memcmp(const void *str1, const void *str2, size_t n)
        if (memcmp(buff, _tag.data(), _tag.size()) == 0)
        {
            // parse from buffer
            const char *data = buff + _tag.size();
            int32_t len_data = len_buff - kChecksumLen - static_cast<int>(_tag.size());
            if (parseFromBuffer(std::string(data, len_data), message))
            {
                error = ErrorCode::kNoError;
            }
            else
            {
                error = ErrorCode::kParseError;
            }
        }
        else
        {
            error = ErrorCode::kUnknownMessageType;
        }
    }
    else
    {
        error = ErrorCode::kCheckSumError;
    }

    return error;
}

//解码
void ProtobufCodecLite::onMessage(const TcpConnectionPtr &conn,
                                  Buffer *buff,
                                  Timestamp receiveTime)
{
    while (buff->readableBytes() >= static_cast<uint32_t>(kHeaderLen + kMinMessageLen))
    {
        // 1.
        const int32_t len_buff = buff->peekInt32();//计算出message长度
        if (len_buff < kMinMessageLen || len_buff > kMaxMessageLen)
        {
            _errorCallback(conn, buff, receiveTime, ErrorCode::kInvalidLength);
            break;
        }
        else if (buff->readableBytes() >= Utils::implicit_cast<size_t>(kHeaderLen + len_buff))//确定已接受完整的message消息
        {
            //_rawCb检验raw_message消息是否正确，如果return false则停止解析protobuf message消息[]
            if (_rawCb && !_rawCb(conn, std::string(buff->peek(), kHeaderLen + len_buff), receiveTime))
            {
                buff->retrieve(kHeaderLen + len_buff);
                continue;
            }

            MessagePtr message(_prototype->New()); //已经知道message的种类了，new一个空的message，
            ErrorCode errorCode = parse(buff->peek() + kHeaderLen, len_buff, message.get());//反序列化到message中
            if (errorCode == ErrorCode::kNoError)
            {
                // try { } catch (...) { }
                _messageCallback(conn, message, receiveTime);
                buff->retrieve(kHeaderLen + len_buff);
            }
            else
            {
                _errorCallback(conn, buff, receiveTime, errorCode);
                break;
            }
        }
        else
        {
            break;
        }
    }
}
